// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

// kthread - An M:N threading library to make applications more concurrent.

// Date: Tue Jul 10 17:40:58 CST 2012

#ifndef KTHREAD_KTHREAD_H
#define KTHREAD_KTHREAD_H

#include <pthread.h>
#include <sys/socket.h>
#include <krpc/kthread/types.h>
#include <krpc/kthread/errno.h>

#if defined(__cplusplus)
#  include <iostream>
#  include <krpc/kthread/mutex.h>        // use kthread_mutex_t in the RAII way
#endif

#include <krpc/kthread/id.h>

__BEGIN_DECLS

// Create kthread `fn(args)' with attributes `attr' and put the identifier into
// `tid'. Switch to the new thread and schedule old thread to run. Use this
// function when the new thread is more urgent.
// Returns 0 on success, errno otherwise.
extern int kthread_start_urgent(kthread_t* __restrict tid,
                                const kthread_attr_t* __restrict attr,
                                void * (*fn)(void*),
                                void* __restrict args);

// Create kthread `fn(args)' with attributes `attr' and put the identifier into
// `tid'. This function behaves closer to pthread_create: after scheduling the
// new thread to run, it returns. In another word, the new thread may take
// longer time than kthread_start_urgent() to run.
// Return 0 on success, errno otherwise.
extern int kthread_start_background(kthread_t* __restrict tid,
                                    const kthread_attr_t* __restrict attr,
                                    void * (*fn)(void*),
                                    void* __restrict args);

// Wake up operations blocking the thread. Different functions may behave
// differently:
//   kthread_usleep(): returns -1 and sets errno to ESTOP if kthread_stop()
//                     is called, or to EINTR otherwise.
//   butex_wait(): returns -1 and sets errno to EINTR
//   kthread_mutex_*lock: unaffected (still blocking)
//   kthread_cond_*wait: wakes up and returns 0.
//   kthread_*join: unaffected.
// Common usage of interruption is to make a thread to quit ASAP.
//    [Thread1]                  [Thread2]
//   set stopping flag
//   kthread_interrupt(Thread2)
//                               wake up
//                               see the flag and quit
//                               may block again if the flag is unchanged
// kthread_interrupt() guarantees that Thread2 is woken up reliably no matter
// how the 2 threads are interleaved.
// Returns 0 on success, errno otherwise.
extern int kthread_interrupt(kthread_t tid, kthread_tag_t tag = KTHREAD_TAG_DEFAULT);

// Make kthread_stopped() on the kthread return true and interrupt the kthread.
// Note that current kthread_stop() solely sets the built-in "stop flag" and
// calls kthread_interrupt(), which is different from earlier versions of
// kthread, and replaceable by user-defined stop flags plus calls to
// kthread_interrupt().
// Returns 0 on success, errno otherwise.
extern int kthread_stop(kthread_t tid);

// Returns 1 iff kthread_stop(tid) was called or the thread does not exist,
// 0 otherwise.
extern int kthread_stopped(kthread_t tid);

// Returns identifier of caller if caller is a kthread, 0 otherwise(Id of a
// kthread is never zero)
extern kthread_t kthread_self(void);

// Compare two kthread identifiers.
// Returns a non-zero value if t1 and t2 are equal, zero otherwise.
extern int kthread_equal(kthread_t t1, kthread_t t2);

// Terminate calling kthread/pthread and make `retval' available to any
// successful join with the terminating thread. This function does not return.
extern void kthread_exit(void* retval) __attribute__((__noreturn__));

// Make calling thread wait for termination of kthread `bt'. Return immediately
// if `bt' is already terminated.
// Notes:
//  - All kthreads are "detached" but still joinable.
//  - *kthread_return is always set to null. If you need to return value
//    from a kthread, pass the value via the `args' created the kthread.
//  - kthread_join() is not affected by kthread_interrupt.
// Returns 0 on success, errno otherwise.
extern int kthread_join(kthread_t bt, void** kthread_return);

// Track and join many kthreads.
// Notice that all kthread_list* functions are NOT thread-safe.
extern int  kthread_list_init(kthread_list_t* list,
                              unsigned size, unsigned conflict_size);
extern void kthread_list_destroy(kthread_list_t* list);
extern int  kthread_list_add(kthread_list_t* list, kthread_t tid);
extern int kthread_list_stop(kthread_list_t* list);
extern int  kthread_list_join(kthread_list_t* list);

// ------------------------------------------
// Functions for handling attributes.
// ------------------------------------------

// Initialize thread attribute `attr' with default attributes.
extern int kthread_attr_init(kthread_attr_t* attr);

// Destroy thread attribute `attr'.
extern int kthread_attr_destroy(kthread_attr_t* attr);

// Initialize kthread attribute `attr' with attributes corresponding to the
// already running kthread `bt'.  It shall be called on unitialized `attr'
// and destroyed with kthread_attr_destroy when no longer needed.
extern int kthread_getattr(kthread_t bt, kthread_attr_t* attr);

// ---------------------------------------------
// Functions for scheduling control.
// ---------------------------------------------

// Get number of worker pthreads
extern int kthread_getconcurrency(void);

// Set number of worker pthreads to `num'. After a successful call,
// kthread_getconcurrency() shall return new set number, but workers may
// take some time to quit or create.
// NOTE: currently concurrency cannot be reduced after any kthread created.
extern int kthread_setconcurrency(int num);

// Get number of worker pthreads by tag
extern int kthread_getconcurrency_by_tag(kthread_tag_t tag);

// Set number of worker pthreads to `num' for specified tag
extern int kthread_setconcurrency_by_tag(int num, kthread_tag_t tag);

// Yield processor to another kthread.
// Notice that current implementation is not fair, which means that 
// even if kthread_yield() is called, suspended threads may still starve.
extern int kthread_yield(void);

// Suspend current thread for at least `microseconds'
// Interruptible by kthread_interrupt().
extern int kthread_usleep(uint64_t microseconds);

// ---------------------------------------------
// Functions for mutex handling.
// ---------------------------------------------

// Initialize `mutex' using attributes in `mutex_attr', or use the
// default values if later is NULL.
// NOTE: mutexattr is not used in current mutex implementation. User shall
//       always pass a NULL attribute.
extern int kthread_mutex_init(kthread_mutex_t* __restrict mutex,
                              const kthread_mutexattr_t* __restrict mutex_attr);

// Destroy `mutex'.
extern int kthread_mutex_destroy(kthread_mutex_t* mutex);

// Try to lock `mutex'.
extern int kthread_mutex_trylock(kthread_mutex_t* mutex);

// Wait until lock for `mutex' becomes available and lock it.
extern int kthread_mutex_lock(kthread_mutex_t* mutex);

// Wait until lock becomes available and lock it or time exceeds `abstime'
extern int kthread_mutex_timedlock(kthread_mutex_t* __restrict mutex,
                                   const struct timespec* __restrict abstime);

// Unlock `mutex'.
extern int kthread_mutex_unlock(kthread_mutex_t* mutex);

// -----------------------------------------------
// Functions for handling conditional variables.
// -----------------------------------------------

// Initialize condition variable `cond' using attributes `cond_attr', or use
// the default values if later is NULL.
// NOTE: cond_attr is not used in current condition implementation. User shall
//       always pass a NULL attribute.
extern int kthread_cond_init(kthread_cond_t* __restrict cond,
                             const kthread_condattr_t* __restrict cond_attr);

// Destroy condition variable `cond'.
extern int kthread_cond_destroy(kthread_cond_t* cond);

// Wake up one thread waiting for condition variable `cond'.
extern int kthread_cond_signal(kthread_cond_t* cond);

// Wake up all threads waiting for condition variables `cond'.
extern int kthread_cond_broadcast(kthread_cond_t* cond);

// Wait for condition variable `cond' to be signaled or broadcast.
// `mutex' is assumed to be locked before.
extern int kthread_cond_wait(kthread_cond_t* __restrict cond,
                             kthread_mutex_t* __restrict mutex);

// Wait for condition variable `cond' to be signaled or broadcast until
// `abstime'. `mutex' is assumed to be locked before.  `abstime' is an
// absolute time specification; zero is the beginning of the epoch
// (00:00:00 GMT, January 1, 1970).
extern int kthread_cond_timedwait(
    kthread_cond_t* __restrict cond,
    kthread_mutex_t* __restrict mutex,
    const struct timespec* __restrict abstime);

// -------------------------------------------
// Functions for handling read-write locks.
// -------------------------------------------

// Initialize read-write lock `rwlock' using attributes `attr', or use
// the default values if later is NULL.
extern int kthread_rwlock_init(kthread_rwlock_t* __restrict rwlock,
                               const kthread_rwlockattr_t* __restrict attr);

// Destroy read-write lock `rwlock'.
extern int kthread_rwlock_destroy(kthread_rwlock_t* rwlock);

// Acquire read lock for `rwlock'.
extern int kthread_rwlock_rdlock(kthread_rwlock_t* rwlock);

// Try to acquire read lock for `rwlock'.
extern int kthread_rwlock_tryrdlock(kthread_rwlock_t* rwlock);

// Try to acquire read lock for `rwlock' or return after specfied time.
extern int kthread_rwlock_timedrdlock(
    kthread_rwlock_t* __restrict rwlock,
    const struct timespec* __restrict abstime);

// Acquire write lock for `rwlock'.
extern int kthread_rwlock_wrlock(kthread_rwlock_t* rwlock);

// Try to acquire write lock for `rwlock'.
extern int kthread_rwlock_trywrlock(kthread_rwlock_t* rwlock);

// Try to acquire write lock for `rwlock' or return after specfied time.
extern int kthread_rwlock_timedwrlock(
    kthread_rwlock_t* __restrict rwlock,
    const struct timespec* __restrict abstime);

// Unlock `rwlock'.
extern int kthread_rwlock_unlock(kthread_rwlock_t* rwlock);

// ---------------------------------------------------
// Functions for handling read-write lock attributes.
// ---------------------------------------------------

// Initialize attribute object `attr' with default values.
extern int kthread_rwlockattr_init(kthread_rwlockattr_t* attr);

// Destroy attribute object `attr'.
extern int kthread_rwlockattr_destroy(kthread_rwlockattr_t* attr);

// Return current setting of reader/writer preference.
extern int kthread_rwlockattr_getkind_np(const kthread_rwlockattr_t* attr,
                                         int* pref);

// Set reader/write preference.
extern int kthread_rwlockattr_setkind_np(kthread_rwlockattr_t* attr,
                                         int pref);


// ----------------------------------------------------------------------
// Functions for handling barrier which is a new feature in 1003.1j-2000.
// ----------------------------------------------------------------------

extern int kthread_barrier_init(kthread_barrier_t* __restrict barrier,
                                const kthread_barrierattr_t* __restrict attr,
                                unsigned count);

extern int kthread_barrier_destroy(kthread_barrier_t* barrier);

extern int kthread_barrier_wait(kthread_barrier_t* barrier);

// ---------------------------------------------------------------------
// Functions for handling thread-specific data. 
// Notice that they can be used in pthread: get pthread-specific data in 
// pthreads and get kthread-specific data in kthreads.
// ---------------------------------------------------------------------

// Create a key value identifying a slot in a thread-specific data area.
// Each thread maintains a distinct thread-specific data area.
// `destructor', if non-NULL, is called with the value associated to that key
// when the key is destroyed. `destructor' is not called if the value
// associated is NULL when the key is destroyed.
// Returns 0 on success, error code otherwise.
extern int kthread_key_create(kthread_key_t* key,
                              void (*destructor)(void* data));

// Delete a key previously returned by kthread_key_create().
// It is the responsibility of the application to free the data related to
// the deleted key in any running thread. No destructor is invoked by
// this function. Any destructor that may have been associated with key
// will no longer be called upon thread exit.
// Returns 0 on success, error code otherwise.
extern int kthread_key_delete(kthread_key_t key);

// Store `data' in the thread-specific slot identified by `key'.
// kthread_setspecific() is callable from within destructor. If the application
// does so, destructors will be repeatedly called for at most
// PTHREAD_DESTRUCTOR_ITERATIONS times to clear the slots.
// NOTE: If the thread is not created by krpc server and lifetime is
// very short(doing a little thing and exit), avoid using kthread-local. The
// reason is that kthread-local always allocate keytable on first call to
// kthread_setspecific, the overhead is negligible in long-lived threads,
// but noticeable in shortly-lived threads. Threads in krpc server
// are special since they reuse keytables from a kthread_keytable_pool_t
// in the server.
// Returns 0 on success, error code otherwise.
// If the key is invalid or deleted, return EINVAL.
extern int kthread_setspecific(kthread_key_t key, void* data);

// Return current value of the thread-specific slot identified by `key'.
// If kthread_setspecific() had not been called in the thread, return NULL.
// If the key is invalid or deleted, return NULL.
extern void* kthread_getspecific(kthread_key_t key);

// Return current kthread tag
extern kthread_tag_t kthread_self_tag(void);

// The first call to kthread_once() by any thread in a process, with a given
// once_control, will call the init_routine() with no arguments. Subsequent
// calls of kthread_once() with the same once_control will not call the
// init_routine(). On return from kthread_once(), it is guaranteed that
// init_routine() has completed. The once_control parameter is used to
// determine whether the associated initialisation routine has been called.
// Returns 0 on success, error code otherwise.
extern int kthread_once(kthread_once_t* once_control, void (*init_routine)());

__END_DECLS

#endif  // KTHREAD_KTHREAD_H
